﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using Utils;

namespace PolarSonicLink
{
    public partial class UserInterface : Form
    {
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void UpdateStatusCallback(string data);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void UpdateProgressCallback(int progress);

        delegate void SetStatusCallback(string data);
        delegate void SetProgressCallback(int progress);

        [DllImport("DSPLib.dll")]
        public static extern int ByteDecode(int restart, int sample, UpdateStatusCallback UpdateStatus);

        public UserInterface()
        {
            InitializeComponent();

            m_graphResolution = Config.GetConfig().GraphResolution;

            try
            {
                XmlSerializer deserializer = new XmlSerializer(typeof(CyclogsUserValues));
                FileInfo file = ApplicationData.User.GetFile("CyclogValues.xml");
                FileStream fileStream = new FileStream(file.FullName, FileMode.Open);
                m_cyclogsUserValues = (CyclogsUserValues) deserializer.Deserialize(fileStream);
                fileStream.Close();
            }
            catch ( Exception ex )
            {
                //MessageBox.Show(ex.Message);
            }

            buttonFile.Visible = false;
            buttonFromXML.Visible = false;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                m_byteAssembler = new ByteAssembler();

                WaveLib.WaveFormat fmt = new WaveLib.WaveFormat(44100, 16, 1);
                m_Recorder = new WaveLib.WaveInRecorder(-1, fmt, bufferReadSize, 3, new WaveLib.BufferDoneEventHandler(DataArrived));

                buttonFile.Enabled = false;
                buttonFromXML.Enabled = false;
                buttonLive.Enabled = false;
                buttonStop.Enabled = true;
            }
            catch
            {
                throw;
            }
        }

        private void DataArrived(IntPtr data, int size)
        {
            if ( m_RecBuffer == null || m_RecBuffer.Length < size )
                m_RecBuffer = new byte[size];
            System.Runtime.InteropServices.Marshal.Copy(data, m_RecBuffer, 0, size);

            ProcessRecordBuffer();
        }

        private void ProcessRecordBuffer()
        {
            double[] sampleBuffer = new double[bufferReadSize / m_graphResolution];

            if ( m_byteAssembler.FinishedDecoding == false )
            {
                for ( int y = 0; y < bufferReadSize; y += 2 )
                {
                    int byte1 = (int) m_RecBuffer[y];
                    int byte2 = (int) m_RecBuffer[y + 1];

                    int sample = byte1 + ( byte2 << 8 );
                    if ( sample > 32767 )
                    {
                        sample -= 65536;
                    }

                    sampleBuffer[y / m_graphResolution] = ( sample / 32768.0 );
                                        
                    int thisByte = ByteDecode(m_byteAssembler.Restart, sample, UpdateStatus);

                    if ( m_byteAssembler.Restart > 0)
                    {
                        m_byteAssembler.Restart = 0;
                    }

                    if ( thisByte >= 0 )
                    {
                        m_byteAssembler.AddByte(thisByte, UpdateStatus, UpdateProgress);
                    }
                    else if ( thisByte == -3 )
                    {
                        UpdateStatus("No more bytes.");
                        //throw new Exception("No more bytes -- do something");
                        m_byteAssembler.FinishedDecoding = true;
                    }
                    else if ( thisByte == -2 )
                    {
                        UpdateStatus("Errors in signal.");
                        m_byteAssembler.FinishedDecoding = true;
                    }

                    if (m_byteAssembler.ValidDecoding == true)
                    {
                        try
                        {
                            XmlSerializer s = new XmlSerializer(typeof(ByteAssembler));
                            FileInfo file = ApplicationData.User.GetFile("ReceivedBytes.xml");
                            TextWriter w = new StreamWriter(file.FullName);
                            s.Serialize(w, m_byteAssembler);
                            w.Close();
                        }
                        catch ( Exception e )
                        {
                            MessageBox.Show(e.Message);
                        }

                        /*Stop();

                        buttonFile.Enabled = true;
                        buttonFromXML.Enabled = true;
                        buttonLive.Enabled = true;
                        buttonStop.Enabled = false;*/

                        DecodeData();  
                    }

                    if ( m_byteAssembler.FinishedDecoding == true )
                    {
                        break;
                    }
                }

                simpleGraph1.UpdateData(sampleBuffer);
            }
        }

        public void UpdateStatus(string data)
        {
            if ( InvokeRequired == false )
            {
                toolStripStatusLabelStatus.Text = data;
            }
            else
            {
                SetStatusCallback d = new SetStatusCallback(UpdateStatus);
                this.Invoke(d, new object[] { data });
            }  
        }

        public void UpdateProgress(int progress )
        {
            if ( InvokeRequired == false )
            {
                toolStripProgressBar.Value = progress;
            }
            else
            {
                SetProgressCallback d = new SetProgressCallback(UpdateProgress);
                this.Invoke(d, new object[] { progress });
            }  
        }

        private void UserInterface_FormClosing(object sender, FormClosingEventArgs e)
        {
            Stop();
        }

        private void Stop()
        {
            timerWAVFileLoader.Enabled = false;

            if ( m_Recorder != null )
            {
                try
                {
                    m_Recorder.Dispose();
                }
                finally
                {
                    m_Recorder = null;
                }
            }
        }

        public static void ReadWholeArray(Stream stream, byte[] data)
        {
            int remaining = data.Length;
            int offset = 0;

            while ( remaining > 0 )
            {
                int read = stream.Read(data, offset, remaining);
                if ( read <= 0 )
                    throw new EndOfStreamException
                        (String.Format("End of stream reached with {0} bytes left to read", remaining));
                remaining -= read;
                offset += read;
            }
        }

        private void buttonFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileOpenDialog = new OpenFileDialog();

            fileOpenDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            //fileOpenDialog.InitialDirectory = "C:\\windows\\media";

            fileOpenDialog.Filter = "WAV files (*.wav)|*.wav|All files (*.*)|*.*";

            if ( fileOpenDialog.ShowDialog() == DialogResult.OK )
            {
                buttonFile.Enabled = false;
                buttonFromXML.Enabled = false;
                buttonLive.Enabled = false;
                buttonStop.Enabled = true;

                m_WAVFileName = fileOpenDialog.FileName;
                m_WAVOffset = 44;

                m_byteAssembler = new ByteAssembler();
                timerWAVFileLoader.Enabled = true;
            }
        }

        private void timerWAVFileLoader_Tick(object sender, EventArgs e)
        {
            FileStream fs = File.OpenRead(m_WAVFileName);

            fs.Seek(m_WAVOffset, SeekOrigin.Begin);

            try
            {
                ReadWholeArray(fs, m_RecBuffer);
                ProcessRecordBuffer();
                m_WAVOffset += bufferReadSize;
            }
            catch ( Exception )
            {
                timerWAVFileLoader.Enabled = false;
            }

            fs.Close();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            Stop();

            buttonFile.Enabled = true;
            buttonFromXML.Enabled = true;
            buttonLive.Enabled = true;
            buttonStop.Enabled = false;
        }

        private void cyclogsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CyclogOptions cyclogsOptionsDlg = new CyclogOptions(m_cyclogsUserValues);

            if ( cyclogsOptionsDlg.ShowDialog() == DialogResult.OK )
            {
                try
                {
                    XmlSerializer s = new XmlSerializer(typeof(CyclogsUserValues));
                    FileInfo file = ApplicationData.User.GetFile("CyclogValues.xml");
                    TextWriter w = new StreamWriter(file.FullName);
                    s.Serialize(w, cyclogsOptionsDlg.m_cyclogsUserValues);
                    w.Close();
                }
                catch ( Exception ex )
                {
                    MessageBox.Show(ex.Message);
                }

                m_cyclogsUserValues = cyclogsOptionsDlg.m_cyclogsUserValues;
            }
        }

        private void buttonFromXML_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileOpenDialog = new OpenFileDialog();

            fileOpenDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            fileOpenDialog.Filter = "XML files (*.xml)|*.xml|All files (*.*)|*.*";

            if ( fileOpenDialog.ShowDialog() == DialogResult.OK )
            {
                buttonFile.Enabled = false;
                buttonFromXML.Enabled = false;
                buttonLive.Enabled = false;
                buttonStop.Enabled = true;

                try
                {
                    XmlSerializer deserializer = new XmlSerializer(typeof(ByteAssembler));
                    FileStream fileStream = new FileStream(fileOpenDialog.FileName, FileMode.Open);
                    m_byteAssembler = (ByteAssembler) deserializer.Deserialize(fileStream);
                    fileStream.Close();
                }
                catch ( Exception ex )
                {
                    MessageBox.Show(ex.Message);
                }

                DecodeData();
            }
        }

        private void DecodeData()
        {
            try
            {
                SessionDecoder sessionDecoder = new SessionDecoder(m_byteAssembler.AssembledData);
                sessionDecoder.Decode();

                SessionViewer sessionViewer = new SessionViewer(sessionDecoder, m_cyclogsUserValues);
                sessionViewer.ShowDialog();
            }
            catch ( Exception ex )
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Stop();
            Close();
        }

        private void enableDebugToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if ( enableDebugToolStripMenuItem.Checked )
            {
                enableDebugToolStripMenuItem.Checked = false;
                buttonFile.Visible = false;
                buttonFromXML.Visible = false;
            }
            else
            {
                enableDebugToolStripMenuItem.Checked = true;
                buttonFile.Visible = true;
                buttonFromXML.Visible = true;
            }
        }

        private static int bufferReadSize = 8192;
        private WaveLib.WaveInRecorder m_Recorder;
        private byte[] m_RecBuffer = new byte[bufferReadSize];
        private int m_WAVOffset = 44; // WAV Header length
        public ByteAssembler m_byteAssembler = new ByteAssembler();
        private CyclogsUserValues m_cyclogsUserValues = new CyclogsUserValues();
        private string m_WAVFileName;
        private int m_graphResolution = 16;
    }
}
